Skip to content

fix(core): accented Latin letters draw in a bundled family at its bundled weights - #7299

Merged
miguel-heygen merged 2 commits into
mainfrom
dcloudpro/accented-latin-bundled-weights
Nov 6, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
dcloudpro/accented-latin-bundled-weights

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

What

Letters beyond Latin-1 (č š ć ž đ, ą ę ł ń ś ź ż, ř ě ů, ğ ı İ, ő ű, ă ș ț, Vietnamese, and the rest of Latin Extended) now draw in the requested family at the weights the bundle ships (400, 700, 900 of Inter, Outfit and the other bundled families). Before, they fell back to the next font in the stack (a serif) at those weights while weight 500 drew correctly.

Why

The bundle embeds only Google's latin subset per weight. The rest comes from a supplementary Google fetch, which for a variable font answers one face with a weight range (font-weight: 100 900). A browser matches an exact-weight face before it looks at a range face, so at 400, 700 and 900 it picks the embedded face, finds no glyph for the letter, and goes to the next font; it never reaches the range face. Weight 500 has no embedded face, so the range face is the match there.

Related work

Builds on the bundled-subset coverage test in deterministicFonts-bundledSubset.test.ts. No open issue or overlapping PR.

How

When a supplement face's weight is a range, bundledFamilyFaceRules also emits it at each bundled weight the range spans, with the same source and unicode-range. Faces with the same family, weight and style form one set in the browser, so the embedded Latin glyphs and the supplement's Latin Extended glyphs are used together.

Test plan

fails without the fix: deterministicFonts-bundledSubset.test.ts > "variable supplement at bundled weights" expects faces at 400, 700, 900 and 100 900; main emits only 100 900, so it fails there (received [ '100 900' ]).

  • Unit tests added/updated: the test above; it also checks that Croatian, Polish, Czech, Turkish, Hungarian, Romanian and Vietnamese letters beyond Latin-1 fall in the face's range.
  • Manual testing performed: rendered a page with Outfit 400/500/700 and Inter 700/900 text in those languages through the CLI before and after, with the real Google fetch. Before: 400 and 700 lines show a serif for the accented letters; after: every line draws in its family.
  • Documentation updated (if applicable): not needed.
  • Comments follow CONTRIBUTING.md "Comments".

Not exercised: offline runs (no supplement fetch, so those letters have no source at any weight), italics, Windows, macOS.

Size

A lone fix nothing open can carry: no other open PR touches the bundled-family face rules.

Before: 400 and 700 lines draw the accented letters in a serif

After: every line draws in its family

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 3866e559. Requesting changes: one blocker. The fix itself looks right.

Blocker

  • The Comments workflow fails at this head, and the failure comes from this diff. The job "A package file's comment share may not rise" reports: RAISED comment share: packages/core/src/fonts/deterministicFonts.ts is 9.4% (201/2144), at the base 9.3% (198/2121). Three comment lines were added: the JSDoc on bundledWeightsWithin and the two-line note in the emit loop. Cut them or move the explanation into names. This also conflicts with the checked "Comments follow CONTRIBUTING.md" box.

What I verified

  • fails without the fix: names deterministicFonts-bundledSubset.test.ts > "variable supplement at bundled weights". I ran it locally. It fails with deterministicFonts.ts from main and passes at head. All 24 files in src/fonts/ pass except the @hyperframes/core/fonts/aliases self-import test, which fails the same way on main because core isn't built locally.
  • Ordering is safe. The new exact-weight copies are emitted before bundledRules, so the bundled Latin faces still win where the ranges overlap.

Non-blocking

  • The "Why" section doesn't describe the real request. For css2?family=Outfit:ital,wght@0,100;…;0,900, Google returns a separate face for each weight, all pointing at the same woff2. It does not return one 100 900 face. The 100 900 range comes from partitionWeightRuns: spansCoveredWeight uses strict bounds, so 300→400→500 never breaks, and the latin-ext faces at 400, 700 and 900 merge into one range. The fix still covers this case because bundledWeightsWithin is inclusive. But the test only mocks the single-range shape. Consider adding the per-weight shape Google actually sends, so the case that really happens is pinned.
  • Size: each non-Latin variable subset is now embedded at every bundled weight, plus the range. For Inter's normal style the non-Latin subsets are about 180 KB of woff2 together. Without text=, that adds about 0.5 MB raw per document, more if italics are bundled. The text= path keeps this small in the common case. A different fix would avoid it: stop merging a run across a covered endpoint, then drop the range copy. That said, the duplication is a fair price for correct output.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at c8c021ff. Approved.

  • The blocker is resolved. Comments now passes. The diff from 3866e559 only removes and shortens comments (1 line added, 3 removed, all in deterministicFonts.ts). The logic and the test are byte-identical, so the earlier result still holds: deterministicFonts-bundledSubset.test.ts > "variable supplement at bundled weights" fails on main and passes with the fix.
  • My non-blocking notes from the last review still stand. First, the "Why" section describes one 100 900 face, but Google actually sends one face per weight, and partitionWeightRuns merges them into that range. Second, the fix duplicates each non-Latin subset at every bundled weight. Neither blocks the merge.
  • At review time, CI, regression, Windows render verification, CodeQL, Player perf and preview-regression were still running on this head. Merge should wait for them.

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 111 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 6011d9d Nov 6, 2026
95 checks passed
@miguel-heygen
miguel-heygen deleted the dcloudpro/accented-latin-bundled-weights branch November 6, 2026 07:35
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view

Adopts heygen-com#7299 (accented Latin letters draw in a bundled family at its
bundled weight), heygen-com#7301 (installing Chrome on a busy Windows PC), heygen-com#7262
(cloud render logs a line a minute while a job holds its status), heygen-com#7265
(video starts a second early and catches up hidden) and heygen-com#7305
(check_report names the finding that stopped the check). Player packed
budget takes upstream's +1655 from heygen-com#7265 since the player bundles core's
media-steer. Defers heygen-com#7302; heygen-com#7261 and heygen-com#7263 do not apply. Watermark
advances to 9e2510b.

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-authored-by: WaterrrForever <miao.yang@heygen.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant